package com.verlet.springbootdemo.aop;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

/**
 * @author verlet
 * @date 2021-03-15 22:30
 */

@Slf4j
@Order(1)
@Aspect
@Component
@ConditionalOnProperty(prefix = "servlet", name = "logAspect", havingValue = "true")
public class LogAspect {

    @Pointcut("execution(* com.verlet.springbootdemo.controller.*.*(..))")
    public void pointcut() {
    }

    /**
     * 前置通知：在目标方法执行之前执行
     */
    @Before(value = "pointcut()")
    public void before(JoinPoint joinPoint) {
        String name = joinPoint.getSignature().getName();
        log.info("{} 方法, 执行 before", name);
    }

    /**
     * 后置通知：在目标方法执行之后执行
     */
    @After(value = "pointcut()")
    public void after(JoinPoint joinPoint) {
        String name = joinPoint.getSignature().getName();
        log.info("{} 方法, 执行 after", name);
    }

    /**
     * 返回通知：获取目标方法的返回值
     *
     * @param joinPoint
     * @param result
     */
    @AfterReturning(value = "pointcut()", returning = "result")
    public void afterReturning(JoinPoint joinPoint, Object result) {
        String name = joinPoint.getSignature().getName();
        log.info("{} 方法, 执行 afterReturning, 返回: {}", name, result);
    }

    /**
     * 异常通知：当目标方法出现异常时，会执行该方法
     *
     * @param joinPoint
     * @param e
     */
    @AfterThrowing(value = "pointcut()", throwing = "e")
    public void afterThrowing(JoinPoint joinPoint, Exception e) {
        String name = joinPoint.getSignature().getName();
        log.error("{} 方法, 执行 afterThrowing", name, e);
    }

    /**
     * 环绕通知：可实现任意通知，可调用 proceedingJoinPoint.proceed() 方法使目标方法继续执行
     *
     * @param proceedingJoinPoint
     * @return
     * @throws Throwable
     */
    @Around(value = "pointcut()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        log.info("around 前执行");
        Object proceed = proceedingJoinPoint.proceed();
        log.info("around 后执行 {}", proceed);
        return proceed;
    }

}
